/*
 * Copyright 2024 NXP
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* System includes */
#include <stdint.h>
#include <uwb_bus_board.h>
#include <uwb_bus_interface.h>

/* Freescale includes*/
#include "board.h"
/* UWB includes */
#include "driver_config.h"
#include "phUwbTypes.h"
#include "phOsalUwb.h"
#include "phUwb_BuildConfig.h"
#include "phUwbErrorCodes.h"
#include "phNxpLogApis_TmlUwb.h"
#include "fsl_lpspi_edma.h"
#include "fsl_lpspi.h"
#define LPSPI_MASTER_CLK_FREQ (CLOCK_GetIpFreq(kCLOCK_Lpspi1))
#define DMA_BASE                      DMA0
#define LPSPI_MASTER_DMA_TX_CHANNEL   0U
#define LPSPI_MASTER_DMA_RX_CHANNEL   1U
#define LPSPI_MASTER_PCS_FOR_TRANSFER (kLPSPI_MasterPcs3)
#define LPSPI_TRANSMIT_EDMA_CHANNEL      30U
#define LPSPI_RECEIVE_EDMA_CHANNEL       29U

#define DMA_DEVICE_INTERRUPT_PRIORITY (4U)

static status_t gtransferStatus;

#define EXAMPLE_LPSPI_MASTER_PCS_FOR_TRANSFER (kLPSPI_MasterPcs3)

/* This semaphore is signaled when SPI write is completed successfully*/
void *mSpiTransferSem = NULL;

volatile bool isTransferCompleted  = false;

extern void vAppEnableSleep(void);
extern void vAppDisableSleep(void);

void LPSPI_MasterUserCallback(LPSPI_Type *base, lpspi_master_edma_handle_t *handle, status_t status, void *userData)
{
    gtransferStatus = status;
   (void)phOsalUwb_ProduceSemaphore(mSpiTransferSem);

}

uwb_bus_status_t uwb_bus_init(uwb_bus_board_ctx_t *pCtx)
{
    lpspi_master_config_t masterConfig;
    edma_config_t userConfig;

    if (pCtx == NULL) {
        LOG_E("uwbs bus context is NULL");
        return kUWB_bus_Status_FAILED;
    }

    CLOCK_SetIpSrc(kCLOCK_Lpspi1, kCLOCK_IpSrcFro192M);
    CLOCK_SetIpSrcDiv(kCLOCK_Lpspi1, kSCG_SysClkDivBy6);

    //CLOCK_EnableClock(kCLOCK_Dma0);
    CLOCK_EnableClock(kCLOCK_Lpspi1);

    /* Init CE */
    if (kUWB_bus_Status_OK != uwb_bus_io_val_set(pCtx, kUWBS_IO_O_ENABLE_HELIOS, kUWBS_IO_State_Low)) {
        LOG_E("Error %s: could not set io value", __FUNCTION__);
        return kUWB_bus_Status_FAILED;
    }

    if (kUWB_bus_Status_OK != uwb_bus_io_val_set(pCtx, kUWBS_IO_O_HELIOS_SYNC, kUWBS_IO_State_Low)) {
        LOG_E("Error %s: could not set io value", __FUNCTION__);
        return kUWB_bus_Status_FAILED;
    }

    /*DMA Mux setting and EDMA init*/
#if defined(FSL_FEATURE_SOC_DMAMUX_COUNT) && FSL_FEATURE_SOC_DMAMUX_COUNT
    /* DMA MUX init*/
    DMAMUX_Init(EXAMPLE_LPSPI_MASTER_DMA_MUX_BASE);

    DMAMUX_SetSource(EXAMPLE_LPSPI_MASTER_DMA_MUX_BASE, EXAMPLE_LPSPI_MASTER_DMA_RX_CHANNEL,
                     EXAMPLE_LPSPI_MASTER_DMA_RX_REQUEST_SOURCE);
    DMAMUX_EnableChannel(EXAMPLE_LPSPI_MASTER_DMA_MUX_BASE, EXAMPLE_LPSPI_MASTER_DMA_RX_CHANNEL);

    DMAMUX_SetSource(EXAMPLE_LPSPI_MASTER_DMA_MUX_BASE, EXAMPLE_LPSPI_MASTER_DMA_TX_CHANNEL,
                     EXAMPLE_LPSPI_MASTER_DMA_TX_REQUEST_SOURCE);
    DMAMUX_EnableChannel(EXAMPLE_LPSPI_MASTER_DMA_MUX_BASE, EXAMPLE_LPSPI_MASTER_DMA_TX_CHANNEL);
#endif

    /* EDMA init*/
    /*
     * userConfig.enableRoundRobinArbitration = false;
     * userConfig.enableHaltOnError = true;
     * userConfig.enableContinuousLinkMode = false;
     * userConfig.enableDebugMode = false;
     */
    EDMA_GetDefaultConfig(&userConfig);
    EDMA_Init(DMA_BASE, &userConfig);

    /* Master config */
    LPSPI_MasterGetDefaultConfig(&masterConfig);
    // SPI_MasterGetDefaultConfig(&masterConfig);
    masterConfig.baudRate           = UWB_SPI_BAUDRATE;
    masterConfig.whichPcs           = UWB_SPI_SSEL;
    masterConfig.pcsActiveHighOrLow = kLPSPI_PcsActiveLow;
    masterConfig.cpol               = kLPSPI_ClockPolarityActiveHigh;
    masterConfig.cpha               = kLPSPI_ClockPhaseFirstEdge;
    masterConfig.pcsToSckDelayInNanoSec        = 15U / (masterConfig.baudRate * 2U);
    masterConfig.lastSckToPcsDelayInNanoSec    = 15U / (masterConfig.baudRate * 2U);
    masterConfig.betweenTransferDelayInNanoSec = 15U / (masterConfig.baudRate * 2U);

    /*Set up lpspi master*/

    memset(&(pCtx->masterTxHandle), 0, sizeof(pCtx->masterTxHandle));
    memset(&(pCtx->masterRxHandle), 0, sizeof(pCtx->masterRxHandle));

    uint32_t srcClock_Hz = LPSPI_MASTER_CLK_FREQ;
    LPSPI_MasterInit(UWB_SPI_BASEADDR, &masterConfig, srcClock_Hz);
    LOG_W("SPI Clock %d", srcClock_Hz);
    NVIC_SetPriority(DMA0_CH0_IRQn, DMA_DEVICE_INTERRUPT_PRIORITY + 2);
    NVIC_SetPriority(DMA0_CH1_IRQn, DMA_DEVICE_INTERRUPT_PRIORITY + 3);

    EDMA_CreateHandle(&(pCtx->masterTxHandle), DMA_BASE, LPSPI_MASTER_DMA_TX_CHANNEL);
    EDMA_CreateHandle(&(pCtx->masterRxHandle), DMA_BASE, LPSPI_MASTER_DMA_RX_CHANNEL);
#if defined(FSL_FEATURE_EDMA_HAS_CHANNEL_MUX) && FSL_FEATURE_EDMA_HAS_CHANNEL_MUX
    EDMA_SetChannelMux(DMA_BASE, LPSPI_MASTER_DMA_TX_CHANNEL,
                       LPSPI_TRANSMIT_EDMA_CHANNEL);
    EDMA_SetChannelMux(DMA_BASE, LPSPI_MASTER_DMA_RX_CHANNEL,
                       LPSPI_RECEIVE_EDMA_CHANNEL);
#endif
    LPSPI_MasterTransferCreateHandleEDMA(UWB_SPI_BASEADDR,
        &pCtx->masterHandle,
        LPSPI_MasterUserCallback,
        NULL,
        &pCtx->masterRxHandle,
        &pCtx->masterTxHandle);

    /* This semaphore is signaled when SPI data is send out completely*/
    if (phOsalUwb_CreateBinSem(&mSpiTransferSem) != UWBSTATUS_SUCCESS) {
        LOG_E("Error: uwb_bus_init(), could not create semaphore mSpiTransferSem\n");
        return kUWB_bus_Status_FAILED;
    }

    /*This semaphore is signaled in the ISR context.*/
    if (phOsalUwb_CreateSemaphore(&pCtx->mIrqWaitSem, 0) != kUWBSTATUS_SUCCESS) {
        LOG_E("Error: uwb_uwbs_tml_init(), could not create semaphore mWaitIrqSem\n");
        return kUWB_bus_Status_FAILED;
    }

    return kUWB_bus_Status_OK;
}

uwb_bus_status_t uwb_bus_deinit(uwb_bus_board_ctx_t *pCtx)
{
    if (pCtx == NULL) {
        LOG_E("uwbs bus context is NULL");
        return kUWB_bus_Status_FAILED;
    }
    LPSPI_Deinit(UWB_SPI_BASEADDR);

    phOsalUwb_DeleteSemaphore(&mSpiTransferSem);
    phOsalUwb_ProduceSemaphore(pCtx->mIrqWaitSem);
    phOsalUwb_Delay(2);
    phOsalUwb_DeleteSemaphore(&pCtx->mIrqWaitSem);
    mSpiTransferSem = NULL;
    phOsalUwb_SetMemory(pCtx, 0, sizeof(uwb_bus_board_ctx_t));
    return kUWB_bus_Status_OK;
}

uwb_bus_status_t uwb_bus_data_nfc_tx(uwb_bus_board_ctx_t *pCtx, uint8_t *pBuf, size_t bufLen)
{
    lpspi_transfer_t xfer;
    status_t status;
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;

    if (pCtx == NULL) {
        LOG_E("uwbs bus context is NULL");
        goto end;
    }

    if (pBuf == NULL || bufLen == 0) {
        goto end;
    }

    LPSPI_MasterTransferPrepareEDMALite(UWB_SPI_BASEADDR, &pCtx->masterHandle, BOARD_NXPNCI_SPI_PCS_TRANSFER | kLPSPI_MasterByteSwap | kLPSPI_MasterPcsContinuous);

    /* Send header */
    xfer.rxData      = NULL;
    xfer.txData   = pBuf;
    xfer.dataSize = bufLen;
    status        = LPSPI_MasterTransferEDMALite(UWB_SPI_BASEADDR, &pCtx->masterHandle, &xfer);
    if (status == kStatus_Success) {
    if (phOsalUwb_ConsumeSemaphore_WithTimeout(mSpiTransferSem, MAX_UWBS_SPI_TRANSFER_TIMEOUT) !=
            UWBSTATUS_SUCCESS) {
            PRINTF("%s : spi transfer timeout", __FUNCTION__);
            goto end;
        }
        if (gtransferStatus != kStatus_Success) {
            goto end;
        }
        bus_status = kUWB_bus_Status_OK;
    }
end:
    return bus_status;
}


uwb_bus_status_t uwb_bus_data_tx(uwb_bus_board_ctx_t *pCtx, uint8_t *pBuf, size_t bufLen)
{
    lpspi_transfer_t xfer;
    status_t status;
    status_t status1;
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;
    vAppDisableSleep();
	
    if (pCtx == NULL) {
    	PRINTF("uwbs bus context is NULL");
        goto end;
    }

    if (pBuf == NULL || bufLen == 0) {
        goto end;
    }

    LPSPI_MasterTransferPrepareEDMALite(UWB_SPI_BASEADDR, &pCtx->masterHandle,EXAMPLE_LPSPI_MASTER_PCS_FOR_TRANSFER | kLPSPI_MasterByteSwap | kLPSPI_MasterPcsContinuous);

    /* Send header */
    xfer.rxData      = NULL;
    xfer.txData   = pBuf;
    xfer.dataSize = bufLen;
    status        = LPSPI_MasterTransferEDMALite(UWB_SPI_BASEADDR, &pCtx->masterHandle, &xfer);

    if (status == kStatus_Success) {
    if (phOsalUwb_ConsumeSemaphore_WithTimeout(mSpiTransferSem, MAX_UWBS_SPI_TRANSFER_TIMEOUT) !=
            UWBSTATUS_SUCCESS) {
    	PRINTF("%s : spi transfer timeout", __FUNCTION__);
            goto end;
        }
        if (gtransferStatus != kStatus_Success) {
            goto end;
        }
        bus_status = kUWB_bus_Status_OK;
    }
end:
    vAppEnableSleep();
    return bus_status;
}

uwb_bus_status_t uwb_bus_data_rx(uwb_bus_board_ctx_t *pCtx, uint8_t *pBuf, size_t pBufLen)
{
    lpspi_transfer_t xfer;
    status_t status;
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;
	
    vAppDisableSleep();
	
    if (pCtx == NULL) {
        LOG_E("uwbs bus context is NULL");
        goto end;
    }

    if (pBuf == NULL || pBufLen == 0) {
        goto end;
    }

    LPSPI_MasterTransferPrepareEDMALite(UWB_SPI_BASEADDR, &pCtx->masterHandle,EXAMPLE_LPSPI_MASTER_PCS_FOR_TRANSFER | kLPSPI_MasterByteSwap | kLPSPI_MasterPcsContinuous);

    /* Send header */
    xfer.txData      = NULL;
    xfer.rxData   = pBuf;
    xfer.dataSize = pBufLen;
    status        = LPSPI_MasterTransferEDMALite(UWB_SPI_BASEADDR, &pCtx->masterHandle, &xfer);
    if (status == kStatus_Success) {
       if (phOsalUwb_ConsumeSemaphore_WithTimeout(mSpiTransferSem, MAX_UWBS_SPI_TRANSFER_TIMEOUT) !=
           UWBSTATUS_SUCCESS) {
           LOG_E("%s : spi transfer timeout", __FUNCTION__);
           goto end;
       }
       if (gtransferStatus != kStatus_Success) {
           goto end;
       }
       bus_status = kUWB_bus_Status_OK;
       }
end:
    vAppEnableSleep();
    return bus_status;
}

uwb_bus_status_t uwb_bus_data_nfc_rx(uwb_bus_board_ctx_t *pCtx, uint8_t *pBuf, size_t pBufLen)
{
    lpspi_transfer_t xfer;
    status_t status;
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;

    uint8_t Tx[pBufLen + 1];
    memset(Tx, 0, pBufLen + 1);

    /*first byte must set to 0xff*/
    memset(Tx, 0xff, pBufLen + 1);

    if (pCtx == NULL) {
        LOG_E("uwbs bus context is NULL");
        goto end;
    }

    if (pBuf == NULL || pBufLen == 0) {
        goto end;
    }

    LPSPI_MasterTransferPrepareEDMALite(UWB_SPI_BASEADDR, &pCtx->masterHandle, BOARD_NXPNCI_SPI_PCS_TRANSFER | kLPSPI_MasterByteSwap | kLPSPI_MasterPcsContinuous);

    /* Send header */
    xfer.txData   = Tx;
    xfer.rxData   = pBuf;
    xfer.dataSize = pBufLen + 1;
    status = LPSPI_MasterTransferEDMALite(UWB_SPI_BASEADDR, &pCtx->masterHandle, &xfer);
    if (status == kStatus_Success) {
       if (phOsalUwb_ConsumeSemaphore_WithTimeout(mSpiTransferSem, MAX_UWBS_SPI_TRANSFER_TIMEOUT) !=
           UWBSTATUS_SUCCESS) {
           LOG_E("%s : spi transfer timeout", __FUNCTION__);
           goto end;
       }
       if (gtransferStatus != kStatus_Success) {
           goto end;
       }
       bus_status = kUWB_bus_Status_OK;
       }
    
end:
    return bus_status;
}

uwb_bus_status_t uwb_bus_reset(uwb_bus_board_ctx_t *pCtx)
{
    /* Not needed here */
    return kUWB_bus_Status_OK;
}

// TODO : this API is not yet calibrated for platform
void uwb_port_DelayinMicroSec(int delay)
{
    volatile int cnt = 6 * delay;
    while (cnt--)
        ;
}
